package de.htwg_konstanz.ebus.framework.impEx;

import java.io.File;
import java.io.StringWriter;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import de.htwg_konstanz.ebus.framework.view.ImpExStatusModel;
import de.htwg_konstanz.ebus.framework.wholesaler.api.bo.BOProduct;
import de.htwg_konstanz.ebus.framework.wholesaler.api.bo.BOPurchasePrice;
import de.htwg_konstanz.ebus.framework.wholesaler.api.bo.BOSalesPrice;
import de.htwg_konstanz.ebus.framework.wholesaler.api.bo.IBOPrice;
import de.htwg_konstanz.ebus.framework.wholesaler.api.boa.PriceBOA;
import de.htwg_konstanz.ebus.framework.wholesaler.api.boa.ProductBOA;

public class XmlExporter implements IExporter {
	private static String namespace = "http://www.bmecat.org/XMLSchema/1.2/bmecat_new_catalog";
	
	private String xmlString;
	private Document xmlDocument;
	private ImpExStatusModel statusModel = null;
	private boolean isTransformationNeeded = false;
	private String searchParam = null;
	private Source xsltSource = null; 
	
	 
	/**
	 * Simply export data without a transformation
	 */
	public XmlExporter() {}
	
	
	/**
	 * @param xslt file
	 * 
	 * If parameter file is supplied a xslt transformation is performed.
	 */
	public XmlExporter(String file) {
		isTransformationNeeded = true;
		xsltSource = new StreamSource(new File(file));
	}
	
	/**
	 * @param searchParam
	 * Perform an export only for those products that contain a certain subString (searchParam)
	 */
	public void performExport(String searchParam) {
		this.searchParam = searchParam;
		performExport();
	}
	
	public void performExport() {
		statusModel = new ImpExStatusModel();
		statusModel.setExport();
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

		DocumentBuilder builder = null;
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		Document doc = builder.newDocument();
		
		
		//doc.getXmlEncoding()
		Element bmecat = doc.createElementNS(namespace, "BMECAT");
		bmecat.setAttribute("version", "1.2");
		bmecat.setAttribute("xmlns:ns2", "http://www.w3.org/2001/XMLSchema-instance");
		doc.appendChild(bmecat);
		
		Element header = doc.createElementNS(namespace, "HEADER");
		bmecat.appendChild(header);
		
		Element catalog = doc.createElementNS(namespace,"CATALOG");
		header.appendChild(catalog);
		
		Element language = doc.createElementNS(namespace,"LANGUAGE");
		catalog.appendChild(language);
		language.setTextContent("deu");
		
		Element catalogID = doc.createElementNS(namespace,"CATALOG_ID");
		catalog.appendChild(catalogID);
		catalogID.setTextContent("HTWG-EBUS-09");
		
		Element catalogVersion = doc.createElementNS(namespace,"CATALOG_VERSION");
		catalog.appendChild(catalogVersion);
		catalogVersion.setTextContent("2.0");
		
		Element catalogName = doc.createElementNS(namespace,"CATALOG_NAME");
		catalog.appendChild(catalogName);
		catalogName.setTextContent("Produktkatalog EBUS 09");
		
		Element supplier = doc.createElementNS(namespace,"SUPPLIER");
		header.appendChild(supplier);
		
		Element supplierName = doc.createElementNS(namespace,"SUPPLIER_NAME");
		supplier.appendChild(supplierName);
		supplierName.setTextContent("T12-Enterprise");
		
		Element t_new_catalog = doc.createElementNS(namespace,"T_NEW_CATALOG");
		bmecat.appendChild(t_new_catalog);
		
		List <BOProduct> products = ProductBOA.getInstance().findAll();
		for (BOProduct product : products) {
			
			if(searchParam == null || product.getShortDescription().contains(searchParam)) {
				statusModel.setProcessedArticles(statusModel.getProcessedArticles()+1);
				
				Element article = doc.createElementNS(namespace,"ARTICLE");
				t_new_catalog.appendChild(article);
				
				Element supplierAid = doc.createElementNS(namespace,"SUPPLIER_AID");
				article.appendChild(supplierAid);
				supplierAid.setTextContent("A" + product.getMaterialNumber());
				
				Element articleDetails = doc.createElementNS(namespace,"ARTICLE_DETAILS");
				article.appendChild(articleDetails);
				
				Element descShort = doc.createElementNS(namespace,"DESCRIPTION_SHORT");
				articleDetails.appendChild(descShort);
				descShort.setTextContent(product.getShortDescription());
				
				Element descLong = doc.createElementNS(namespace,"DESCRIPTION_LONG");
				articleDetails.appendChild(descLong);
				descLong.setTextContent(product.getLongDescription());
				
				Element ean = doc.createElementNS(namespace,"EAN");
				articleDetails.appendChild(ean);
				ean.setTextContent(product.getMaterialNumber().toString());
				
				// Hardcoded. This actually pretty stinks...
				Element articleOrderDetails = doc.createElementNS(namespace,"ARTICLE_ORDER_DETAILS");
				article.appendChild(articleOrderDetails);
				Element orderUnit = doc.createElementNS(namespace,"ORDER_UNIT");
				articleOrderDetails.appendChild(orderUnit);
				orderUnit.setTextContent("PK");
				Element contentUnit = doc.createElementNS(namespace,"CONTENT_UNIT");
				articleOrderDetails.appendChild(contentUnit);
				contentUnit.setTextContent("C62");
				Element noCuPerOu = doc.createElementNS(namespace,"NO_CU_PER_OU");
				articleOrderDetails.appendChild(noCuPerOu);
				noCuPerOu.setTextContent("10");
				
						
				Element articlePriceDetails = doc.createElementNS(namespace,"ARTICLE_PRICE_DETAILS");
				article.appendChild(articlePriceDetails);
				
				IBOPrice iboPrice = null;
	
				List<IBOPrice> priceList = PriceBOA.getInstance().findAll();
		     	for (IBOPrice productPrice : priceList) {
		      
		     		if (productPrice instanceof BOSalesPrice) {
		     			BOSalesPrice salesPrice = (BOSalesPrice) productPrice;
		     			if (salesPrice.getProduct().getMaterialNumber().equals(product.getMaterialNumber())) {
			     			iboPrice = product.getSalesPrice(salesPrice.getCountry());
			     			appendPriceDetails(doc, articlePriceDetails, iboPrice);
		     			}
		     		}
		     		
		     		if (productPrice instanceof BOPurchasePrice) {
		     			BOPurchasePrice purchasePrice = (BOPurchasePrice) productPrice;
		     			if (purchasePrice.getProduct().getMaterialNumber().equals(product.getMaterialNumber())) {
		     				iboPrice = product.getPurchasePrice(purchasePrice.getCountry());
			     			appendPriceDetails(doc, articlePriceDetails, iboPrice);
		     			}
		     		}
		     	}			
			}
		}
		
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer optimusPrime = null;
		try {
			if (isTransformationNeeded) {
				optimusPrime = transformerFactory.newTransformer(xsltSource);
			} else {
				optimusPrime = transformerFactory.newTransformer();
			}
		} catch (TransformerConfigurationException e) {
			System.out.println("Optimus Prime couldn't be fabricated");
			e.printStackTrace();
		}
		
		StringWriter sw = new StringWriter();
		StreamResult result = new StreamResult(sw);
		
		DOMSource source = new DOMSource(doc);
		optimusPrime.setOutputProperty(OutputKeys.ENCODING, "iso-8859-1");
		
		try {
			optimusPrime.transform(source, result);
		} catch (TransformerException e) {
			System.out.println("Optimus Prime couldn't transform");
			e.printStackTrace();
		}
		
		//set attribute values for getters
		this.xmlString = sw.toString();
		this.xmlDocument = doc;
	}

	/**
	 * Generate subelements of ARTICLE_PRICE_DETAILS
	 * @param doc
	 * @param articlePriceDetails
	 * @param iboPrice
	 */
	private void appendPriceDetails(Document doc, Element articlePriceDetails, IBOPrice iboPrice) {
		Element articlePrice = doc.createElementNS(namespace,"ARTICLE_PRICE");
		articlePrice.setAttribute("price_type", iboPrice.getPricetype());
		articlePriceDetails.appendChild(articlePrice);
		
		Element priceAmount = doc.createElementNS(namespace,"PRICE_AMOUNT");
		priceAmount.setTextContent(iboPrice.getAmount().toString());
		articlePrice.appendChild(priceAmount);
		
		Element priceCurrency = doc.createElementNS(namespace,"PRICE_CURRENCY");
		priceCurrency.setTextContent(iboPrice.getCountry().getCurrency().getCode());
		articlePrice.appendChild(priceCurrency);
		
		Element tax = doc.createElementNS(namespace,"TAX");
		tax.setTextContent(iboPrice.getTaxrate().toString());
		articlePrice.appendChild(tax);
		
		Element territory = doc.createElementNS(namespace,"TERRITORY");
		territory.setTextContent(iboPrice.getCountry().getIsocode());
		articlePrice.appendChild(territory);
	}


	public ImpExStatusModel getStatusModel() {
		return statusModel;
	}


	public String getXmlString() {
		return xmlString;
	}
	

	public Document getXmlDocument() {
		return xmlDocument;
	}

}
